package main

import (
	"fmt"
	"runtime"
	"sync"
	"time"

	"github.com/garyburd/redigo/redis"
)

var w sync.WaitGroup

type DbnoPool struct {
	pool redis.Pool
	dbno int
}

func (p *DbnoPool) Get() redis.Conn {
	conn := p.pool.Get()
	conn.Do("SELECT", p.dbno)
	return conn
}

func newRdsPool(server, auth string, dbno int) *DbnoPool {
	pool := &DbnoPool{
		redis.Pool{
			MaxIdle:     100,
			MaxActive:   30,
			IdleTimeout: 60 * time.Second,
			Dial: func() (redis.Conn, error) {
				c, err := redis.Dial("tcp", server)
				if err != nil {
					return nil, err
				}
				if auth == "" {
					return c, err
				}
				if _, err := c.Do("AUTH", auth); err != nil {
					c.Close()
					return nil, err
				}
				return c, err
			},
			TestOnBorrow: func(c redis.Conn, t time.Time) error {
				_, err := c.Do("PING")
				return err
			},
		},
		dbno,
	}
	return pool
}

func g1(r redis.Conn) {
	var lock int64
	var lock_timeout int64 = 2
	var lock_time int64
	var now int64
	for lock != 1 {
		now = time.Now().Unix()
		lock_time = now + lock_timeout
		lock, err1 := redis.Int64(r.Do("setnx", "foo", lock_time))
		lockValue1, err2 := redis.Int64(r.Do("get", "foo"))
		// 成功
		if lock == 1 && err1 == nil {
			// 跳出
			break
		} else {
			// now比上一次大
			if now > lockValue1 && err2 == nil {
				lockValue2, err3 := redis.Int64(r.Do("getset", "foo", lock_time))
				if err3 == nil && now > lockValue2 {
					break
				} else {
					// 没有抢到锁
					fmt.Println(`g1 not get lock`)
					time.Sleep(1000 * time.Millisecond)
				}

			} else {
				fmt.Println(`g1 not get lock`)
				time.Sleep(1000 * time.Millisecond)
			}
		}
	}
	for i := 0; i < 5; i++ {
		if _, err := redis.String(r.Do("set", "hello", "1")); err != nil {
			fmt.Println(err)
		}
		fmt.Println(`g1 now work... `)
		time.Sleep(1 * time.Second)
	}
	if time.Now().Unix() < lock_time {
		if _, err4 := redis.Int64(r.Do("del", "foo")); err4 != nil {
			fmt.Println(err4)
		}
	}
	// Done:相当于Add(-1)
	w.Done()
}

func g2(r redis.Conn) {
	var lock int64
	var lock_timeout int64 = 2
	var lock_time int64
	var now int64
	for lock != 1 {
		now = time.Now().Unix()
		lock_time = now + lock_timeout
		lock, err1 := redis.Int64(r.Do("setnx", "foo", lock_time))
		lockValue1, err2 := redis.Int64(r.Do("get", "foo"))
		if lock == 1 && err1 == nil {
			break
		} else {
			if now > lockValue1 && err2 == nil {
				lockValue2, err3 := redis.Int64(r.Do("getset", "foo", lock_time))
				if err3 == nil && now > lockValue2 {
					break
				} else {
					fmt.Println(`g2 not get lock`)
					time.Sleep(1000 * time.Millisecond)
				}
			} else {
				fmt.Println(`g2 not get lock`)
				time.Sleep(1000 * time.Millisecond)
			}
		}
	}
	for i := 0; i < 5; i++ {
		if _, err := redis.String(r.Do("set", "hello", "2")); err != nil {
			fmt.Println(err)
		}
		fmt.Println(`g2 now work... `)
		time.Sleep(1 * time.Second)
	}
	if time.Now().Unix() < lock_time {
		if _, err4 := redis.Int64(r.Do("del", "foo")); err4 != nil {
			fmt.Println(err4)
		}
	}
	// Done:相当于Add(-1)
	w.Done()
}

func main() {
	w.Add(2)
	runtime.GOMAXPROCS(runtime.NumCPU())
	var rc1 redis.Conn = newRdsPool(`10.42.0.23:6379`, "sOd3bZBFgdVyGRWC", 3).Get()
	var rc2 redis.Conn = newRdsPool(`10.42.0.23:6379`, "sOd3bZBFgdVyGRWC", 3).Get()
	defer rc1.Close()
	defer rc2.Close()
	go g1(rc1)
	go g2(rc2)
	w.Wait()
}
